namespace cxxcall
{

void av_data::_Reg_struct_ret ()
{
  if (this->rsize <= 4)
    this->flags |= reg_struct_ret;
  else
#ifndef __armhf__
    *this->curp++ = (uintptr_t)this->raddr;
#else
    this->args[this->ianum++] = (uintptr_t)this->raddr;
#endif
}

bool av_data::arg_t (intptr_t val)
{
#ifdef __armhf__
  if (this->ianum < IARG_NUM)
    {
      this->args[this->ianum++] = val;
      return (true);
    }
#endif
  if (this->curp >= this->endp)
    return (this->set_overflow ());

  *this->curp++ = val;
  return (true);
}

bool av_data::arg (long long val)
{
  uintptr_t *ep = nullptr;
  const size_t LLALIGN = alignof (long long);

#ifdef __armhf__
  auto ia = (this->ianum + sizeof (val) / WSIZE + LLALIGN / WSIZE - 1) &
    -(intptr_t)(LLALIGN / WSIZE);

  if ((int)ia <= IARG_NUM)
    {
      this->ianum = (int)ia;
      memcpy ((char *)&this->args[ia] - sizeof (val), &val, sizeof (val));
      return (true);
    }
  else if (this->curp == &this->args[IARG_NUM])
    ep = (uintptr_t *)(((uintptr_t)&this->args[this->ianum] +
      sizeof (val) + LLALIGN - 1) & -(intptr_t)LLALIGN);
#endif

  if (ep == nullptr)
    ep = (uintptr_t *)(((uintptr_t)this->curp +
      sizeof (val) + LLALIGN - 1) & -(intptr_t)LLALIGN);

  if (ep > this->endp)
    return (this->set_overflow ());

  memcpy ((char *)ep - sizeof (val), &val, sizeof (val));
  this->curp = ep;
  this->ianum = IARG_NUM;
  return (true);
}

bool av_data::arg (float val)
{
#ifdef __armhf__
  if (this->fanum < 16)
    {
      this->farg_mask |= 1 << this->fanum;
      this->fargs[this->fanum++] = val;
      return (true);
    }

#endif

  if (this->curp >= this->endp)
    return (this->set_overflow ());

  memcpy (this->curp++, &val, sizeof (val));
  return (true);
}

bool av_data::arg (double val)
{
#ifdef __armhf__
  if (this->fanum % 2)
    ++this->fanum;

  if (this->fanum < 16)
    {
      this->darg_mask |= 1 << (this->fanum / 2);
      this->dargs[this->fanum / 2] = val;
      this->fanum += 2;
      return (true);
    }

#endif

  if (this->curp + 1 >= this->endp)
    return (this->set_overflow ());

  memcpy (this->curp, &val, sizeof (val));
  this->curp += 2;
  return (true);
}

bool av_data::arg (const void *ptr, size_t size, size_t align)
{
  uintptr_t *ep = nullptr;

#ifndef __armhf__
#  ifdef __ARMEL__

  ep = (uintptr_t *)(((uintptr_t)this->curp + size +
    align - 1) & -(intptr_t)align);

  if (ep > this->endp)
    return (this->set_overflow ());

  memcpy (ep, ptr, size);
  this->curp = (uintptr_t *)(((uintptr_t)ep + WSIZE - 1) & ~(WSIZE - 1));

#  else

  ep = (uintptr_t *)(((((uintptr_t)this->curp + size +
    align - 1) & -(intptr_t)align) + WSIZE - 1) & ~(WSIZE -1));

  if (ep > this->endp)
    return (this->set_overflow ());

  this->curp = ep;
  memcpy ((char *)ep - size, ptr, size);

#  endif


#elif defined (__ARMEL__)

  auto ia = (this->ianum * WSIZE + size + align - 1) & -(intptr_t)align;
  if (ia <= IARG_NUM * WSIZE)
    {
      this->ianum += ((((size + align - 1) & -(intptr_t)align) &
        WSIZE - 1) & ~(WSIZE - 1)) / WSIZE;
      memcpy ((char *)&this->args[this->ianum] - size, ptr, size);
      return (true);
    }
  else
    ep = this->curp == &this->args[IARG_NUM] ?
      &this->args[this->ianum] : this->curp;

  ep = (uintptr_t *)(((((uintptr_t)ep + size + align - 1) &
    -(intptr_t)align) + WSIZE - 1) & ~(WSIZE - 1));

  if (ep > this->endp)
    return (this->set_overflow ());

  memcpy ((char *)ep - size, ptr, size);
  this->curp = ep;
  this->ianum = IARG_NUM;

#else

  auto ia = (this->ianum * WSIZE + size + align - 1) & -(intptr_t)align;
  if (ia <= IARG_NUM * WSIZE)
    {
      this->ianum += ((((size + align - 1) & -(intptr_t)align) +
        WSIZE - 1) & ~(WSIZE - 1)) / WSIZE;
      memcpy ((char *)&this->args[this->ianum] - size, ptr, size);
      return (true);
    }
  else
    ep = this->curp == &this->args[IARG_NUM] ?
      &this->args[this->ianum] : this->curp;

  ep = (uintptr_t *)(((uintptr_t)ep + size + align - 1) & -(intptr_t)align);
  auto e2 = (uintptr_t *)(((uintptr_t)ep + WSIZE - 1) & ~(WSIZE - 1));

  if (e2 > this->endp)
    return (this->set_overflow ());

  memcpy (ep, ptr, size);
  this->curp = e2;
  this->ianum = IARG_NUM;

#endif

  return (true);
}

} // namespace cxxcall
